package com.itfreer.workdate;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Map;

/**
 * 工作日计算服务类，提供以下服务 1.有多少工作天数(天数列表)，给一个月份，或是一个开始结束时间；
 * 2.有多少休息天数(天数列表)，给一个月份，或是一个开始结束时间；
 * 3.计算工作天数据(不包休息日)，以半天为单位。并根据列入的时间，标记出具体x天工作内容，以半天为单位。（应用于请假，外出情况）
 * 4.给两个时间(上下班时间)，计算是否迟到，是否早退，并计算具体时间。并根据列入的时间，标记出具体x天工作内容，以半天为单位(不包休息日)。
 * 5.有多少工作时间，给一个开始结束时间，计算工作时间，去掉所有休息时间，精确到分钟；（应用于工作流时效计算）
 */
public class CalculateServerImp {

	private static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
	private static DateFormat format2 = new SimpleDateFormat("yyyy-MM-dd");

	// 设置上班时间：该处时间可以根据实际情况进行调整
	int abh = 9; // 上午上班时间,小时
	int abm = 0; // 上午上班时间,分钟
	int aeh = 12; // 上午下班时间，小时
	int aem = 0; // 上午下班时间，分钟

	int pbh = 14; // 下午上班时间，小时
	int pbm = 0; // 下午上班时间，分钟
	int peh = 18; // 下午下班时间，小时
	int pem = 0; // 下午下班时间，分钟

	// 每天上班分钟
	int h1 = abh * 60 + abm;
	int h2 = aeh * 60 + aem;
	int h3 = pbh * 60 + pbm;
	int h4 = peh * 60 + pem;
	int hoursPerDay = h2 - h1 + (h4 - h3);

	// 节假日 new String[] { "2019-05-01", "2019-05-02", "2019-05-03" });
	public List<String> holiday = new ArrayList<>();
	//特殊工作日
	public List<String> speworkday = new ArrayList<>();

	public CalculateServerImp() {
		holiday.add("2019-01-01");
		holiday.add("2019-02-04");
		holiday.add("2019-02-05");
		holiday.add("2019-02-06");
		holiday.add("2019-02-07");
		holiday.add("2019-02-08");
		holiday.add("2019-02-09");
		holiday.add("2019-02-10");
		holiday.add("2019-04-05");
		holiday.add("2019-04-06");
		holiday.add("2019-04-07");
		holiday.add("2019-05-01");
		holiday.add("2019-05-02");
		holiday.add("2019-05-03");
		holiday.add("2019-05-04");
		holiday.add("2019-06-07");
		holiday.add("2019-06-08");
		holiday.add("2019-06-09");
		holiday.add("2019-09-13");
		holiday.add("2019-09-14");
		holiday.add("2019-09-15");
		holiday.add("2019-10-01");
		holiday.add("2019-10-02");
		holiday.add("2019-10-03");
		holiday.add("2019-10-04");
		holiday.add("2019-10-05");
		holiday.add("2019-10-06");
		holiday.add("2019-10-07");
		
		speworkday.add("2019-02-02");
		speworkday.add("2019-02-03");
		speworkday.add("2019-04-28");
		speworkday.add("2019-05-05");
		speworkday.add("2019-09-29");
		speworkday.add("2019-10-12");
	}
	
	/**
	 * 4.给两个时间(上下班时间)，计算是否迟到，是否早退，并计算具体时间。并根据列入的时间，标记出具体x天工作内容，以半天为单位(不包休息日)。
	 * 
	 * @param beginTime
	 * @param endTime
	 * @param result
	 */
	public void workTimes(String beginTime, String endTime, Map<String, DayInfo> result) throws ParseException {
		Calendar startDay = Calendar.getInstance();
		Calendar endDay = Calendar.getInstance();
		startDay.setTime(format.parse(beginTime));
		endDay.setTime(format.parse(endTime));

		// 若开始时间晚于结束时间，返回 null
		if (startDay.getTime().getTime() > endDay.getTime().getTime()) {
			return;
		}

		int sYear = startDay.get(Calendar.YEAR); // 获取年
		int sMonth = startDay.get(Calendar.MONTH) + 1; // 获取月份，0表示1月份
		int sDay = startDay.get(Calendar.DAY_OF_MONTH); // 获取当前天数
		int sTime = startDay.get(Calendar.HOUR_OF_DAY); // 获取当前小时
		int sMin = startDay.get(Calendar.MINUTE); // 获取当前分钟

		int eYear = endDay.get(Calendar.YEAR); // 获取年
		int eMonth = endDay.get(Calendar.MONTH) + 1; // 获取月份，0表示1月份
		int eDay = endDay.get(Calendar.DAY_OF_MONTH); // 获取当前天数
		int eTime = endDay.get(Calendar.HOUR_OF_DAY); // 获取当前小时
		int eMin = endDay.get(Calendar.MINUTE); // 获取当前分钟

		// 如果是同一天
		if (sYear == eYear && sMonth == eMonth && sDay == eDay) {
			String day = format2.format(startDay.getTime());
			// 非工作日，不处理
			if (holiday.contains(day)) {
				return;
			}
			
			DayInfo dayInfo = null;
			if (result.containsKey(day)) {
				dayInfo = result.get(day);
			} else {
				dayInfo = new DayInfo();
				result.put(day, dayInfo);
			}

			// 上班时间
			int sTM = sTime * 60 + sMin;
			int eTM = eTime * 60 + eMin;
			// 下班卡早于上午上班时间，不处理
			if (eTM < h1) {
				return;
			}

			if (sTM <= h1) {
				// 上午正常打卡
				dayInfo.getMorning().setIsWork(true);
				dayInfo.getMorning().setStartWork(true);

				// 处理下班卡
				if (eTM < h3) {
					dayInfo.getMorning().setEndWork(true);
					// 上午就走了
					if (eTM < h2) { // 并且是早退
						dayInfo.getMorning().setLeaveEarlyTime(h2 - eTM);
					}
				} else if (eTM < h4) {
					// 下午走，但是早退
					dayInfo.getMorning().setEndWork(true);
					dayInfo.getAfternoon().setIsWork(true);
					dayInfo.getAfternoon().setStartWork(true);
					dayInfo.getAfternoon().setEndWork(true);
					dayInfo.getAfternoon().setLeaveEarlyTime(h4 - eTM);
				} else {
					// 正常下班
					dayInfo.getMorning().setEndWork(true);
					dayInfo.getAfternoon().setIsWork(true);
					dayInfo.getAfternoon().setStartWork(true);
					dayInfo.getAfternoon().setEndWork(true);
				}
			} else if (sTM > h1 && sTM <= h2) {
				// 上午打卡，但迟到
				dayInfo.getMorning().setIsWork(true);
				dayInfo.getMorning().setStartWork(true);
				dayInfo.getMorning().setLateTime(sTM - h1);

				// 处理下班卡
				if (eTM < h3) {
					dayInfo.getMorning().setEndWork(true);
					// 上午就走了
					if (eTM < h2) { // 并且是早退
						dayInfo.getMorning().setLeaveEarlyTime(h2 - eTM);
					}
				} else if (eTM < h4) {
					// 下午走，但是早退
					dayInfo.getMorning().setEndWork(true);
					dayInfo.getAfternoon().setIsWork(true);
					dayInfo.getAfternoon().setStartWork(true);
					dayInfo.getAfternoon().setEndWork(true);
					dayInfo.getAfternoon().setLeaveEarlyTime(h4 - eTM);
				} else {
					// 正常下班
					dayInfo.getMorning().setEndWork(true);
					dayInfo.getAfternoon().setIsWork(true);
					dayInfo.getAfternoon().setStartWork(true);
					dayInfo.getAfternoon().setEndWork(true);
				}
			} else if (sTM <= h3) {
				if (eTM < h3) {
					// 中午就走了，不处理
					return;
				}
				
				// 下午打卡，正常打卡
				dayInfo.getAfternoon().setIsWork(true);
				dayInfo.getAfternoon().setStartWork(true);

				// 处理下班卡
				if (eTM < h4) {
					// 下午走，但是早退
					dayInfo.getAfternoon().setEndWork(true);
					dayInfo.getAfternoon().setLeaveEarlyTime(h4 - eTM);
				} else {
					// 正常下班
					dayInfo.getAfternoon().setEndWork(true);
				}
			} else if (sTM > h3 && sTM <= h4) {
				// 下午打上班卡，但迟到
				dayInfo.getAfternoon().setIsWork(true);
				dayInfo.getAfternoon().setStartWork(true);
				dayInfo.getAfternoon().setLateTime(sTM - h3);
				
				// 处理下班卡
				if (eTM < h4) {
					// 下午走，但是早退
					dayInfo.getAfternoon().setEndWork(true);
					dayInfo.getAfternoon().setLeaveEarlyTime(h4 - eTM);
				} else {
					// 正常下班
					dayInfo.getAfternoon().setEndWork(true);
				}
			} else {
				// 上班卡无效，不处理
			}
		} else {
			return;
		}
	}

	/**
	 * 3.计算工作天数据(不包休息日)，以半天为单位。并根据列入的时间，标记出具体x天工作内容，以半天为单位。（应用于请假，外出情况）
	 * 
	 * @param beginTime
	 * @param endTime
	 * @return
	 * @throws ParseException
	 */
	public float workDays(String beginTime, String endTime, Map<String, DayInfo> result, String info)
			throws ParseException {
		Calendar startDay = Calendar.getInstance();
		Calendar endDay = Calendar.getInstance();
		startDay.setTime(format.parse(beginTime));
		endDay.setTime(format.parse(endTime));

		// 若开始时间晚于结束时间，返回 null
		if (startDay.getTime().getTime() > endDay.getTime().getTime()) {
			return 0F;
		}

		int sYear = startDay.get(Calendar.YEAR); // 获取年
		int sMonth = startDay.get(Calendar.MONTH) + 1; // 获取月份，0表示1月份
		int sDay = startDay.get(Calendar.DAY_OF_MONTH); // 获取当前天数
		int sTime = startDay.get(Calendar.HOUR_OF_DAY); // 获取当前小时
		int sMin = startDay.get(Calendar.MINUTE); // 获取当前分钟

		int eYear = endDay.get(Calendar.YEAR); // 获取年
		int eMonth = endDay.get(Calendar.MONTH) + 1; // 获取月份，0表示1月份
		int eDay = endDay.get(Calendar.DAY_OF_MONTH); // 获取当前天数
		int eTime = endDay.get(Calendar.HOUR_OF_DAY); // 获取当前小时
		int eMin = endDay.get(Calendar.MINUTE); // 获取当前分钟

		float count = 0F;
		// 如果是同一天
		if (sYear == eYear && sMonth == eMonth && sDay == eDay) {
			String day = format2.format(startDay.getTime());
			if (!holiday.contains(day)) {
				DayInfo dayInfo = null;
				if (result.containsKey(day)) {
					dayInfo = result.get(day);
				} else {
					dayInfo = new DayInfo();
					result.put(day, dayInfo);
				}
				// 处理第一天
				first(sTime, sMin, dayInfo, info, true);
				// 处理最后一天
				last(eTime, eMin, dayInfo, info, true);
				count += getCount(dayInfo);
			}
		} else if (sYear == eYear && sMonth == eMonth && sDay + 1 == eDay) { // 相差一天
			String day = format2.format(startDay.getTime());
			if (!holiday.contains(day)) {
				DayInfo dayInfo = null;
				if (result.containsKey(day)) {
					dayInfo = result.get(day);
				} else {
					dayInfo = new DayInfo();
					result.put(day, dayInfo);
				}
				// 处理第一天
				first(sTime, sMin, dayInfo, info, false);
				count += getCount(dayInfo);
			}

			day = format2.format(endDay.getTime());
			if (!holiday.contains(day)) {
				DayInfo dayInfo = null;
				if (result.containsKey(day)) {
					dayInfo = result.get(day);
				} else {
					dayInfo = new DayInfo();
					result.put(day, dayInfo);
				}
				// 处理最后一天
				last(eTime, eMin, dayInfo, info, false);
				count += getCount(dayInfo);
			}
		} else {
			String day = format2.format(startDay.getTime());
			if (!holiday.contains(day)) {
				DayInfo dayInfo = null;
				if (result.containsKey(day)) {
					dayInfo = result.get(day);
				} else {
					dayInfo = new DayInfo();
					result.put(day, dayInfo);
				}
				// 处理第一天
				first(sTime, sMin, dayInfo, info, false);
				count += getCount(dayInfo);
			}

			startDay.add(Calendar.DAY_OF_MONTH, 1);
			endDay.add(Calendar.DAY_OF_MONTH, -1);
			while (startDay.getTime().getTime() <= endDay.getTime().getTime()) {
				day = format2.format(startDay.getTime());
				if (!holiday.contains(day)) {
					DayInfo dayInfo = null;
					if (result.containsKey(day)) {
						dayInfo = result.get(day);
					} else {
						dayInfo = new DayInfo();
						result.put(day, dayInfo);
					}
					dayInfo.getAfternoon().setIsWork(true);
					dayInfo.getAfternoon().setWork(info);
					dayInfo.getMorning().setIsWork(true);
					dayInfo.getMorning().setWork(info);
					count += 1;

				}
				startDay.add(Calendar.DAY_OF_MONTH, 1);
			}

			endDay.add(Calendar.DAY_OF_MONTH, 1);
			day = format2.format(endDay.getTime());
			if (!holiday.contains(day)) {
				DayInfo dayInfo = null;
				if (result.containsKey(day)) {
					dayInfo = result.get(day);
				} else {
					dayInfo = new DayInfo();
					result.put(day, dayInfo);
				}
				// 处理最后一天
				last(eTime, eMin, dayInfo, info, false);
				count += getCount(dayInfo);
			}
		}

		return count;
	}

	private float getCount(DayInfo dayInfo) {
		if (!dayInfo.getMorning().getIsWork() && !dayInfo.getAfternoon().getIsWork()) {
			return 0;
		} else if (dayInfo.getMorning().getIsWork() && dayInfo.getAfternoon().getIsWork()) {
			return 1;
		} else {
			return 0.5F;
		}
	}

	private void first(int time, int min, DayInfo dayInfo, String info, boolean one) {
		// 处理第一天
		int sTM = time * 60 + min;
		if (sTM <= h2) {
			// 上午开始
			dayInfo.getMorning().setIsWork(true);
			dayInfo.getMorning().setWork(info);
			if (!one) {
				dayInfo.getAfternoon().setIsWork(true);
				dayInfo.getAfternoon().setWork(info);
			}
		} else if (sTM <= h4) {
			// 下午开始
			dayInfo.getAfternoon().setIsWork(true);
			dayInfo.getAfternoon().setWork(info);
		} else {
			// 下午下班后开始，不计算
		}
	}

	private void last(int time, int min, DayInfo dayInfo, String info, boolean one) {
		int eTM = time * 60 + min;
		if (eTM <= h1) {
			// 上午上班前结束，不处理
		} else if (eTM <= h2) {
			// 上午结束
			dayInfo.getMorning().setIsWork(true);
			dayInfo.getMorning().setWork(info);
		} else if (eTM <= h3) {
			// 中午结束，算上午结束
			dayInfo.getMorning().setIsWork(true);
			dayInfo.getMorning().setWork(info);
		} else {
			// 下午结束
			dayInfo.getAfternoon().setIsWork(true);
			dayInfo.getAfternoon().setWork(info);
			if (!one) {
				dayInfo.getMorning().setIsWork(true);
				dayInfo.getMorning().setWork(info);
			}
		}
	}

	/**
	 * 有多少工作天数，给一个开始结束时间
	 * 
	 * @return
	 * @throws ParseException
	 */
	public List<String> workDays(String beginTime, String endTime) throws ParseException {
		Calendar startDay = Calendar.getInstance();
		Calendar endDay = Calendar.getInstance();
		startDay.setTime(format2.parse(beginTime));
		endDay.setTime(format2.parse(endTime));

		return workDays(startDay, endDay);
	}

	/**
	 * 有多少工作天数，给一个月份
	 * 
	 * @return
	 * @throws ParseException
	 */
	public List<String> workDays(String beginTime) throws ParseException {
		Calendar startDay = Calendar.getInstance();
		startDay.setTime(format2.parse(beginTime + "-01"));
		int last = startDay.getActualMaximum(Calendar.DAY_OF_MONTH); // 获取本月最大天数
		Calendar endDay = Calendar.getInstance();
		endDay.setTime(format2.parse(beginTime + "-" + last));

		return workDays(startDay, endDay);
	}

	/**
	 * 有多少工作天数，给一个开始结束时间
	 * 
	 * @return
	 * @throws ParseException
	 */
	public List<String> workDays(Calendar startDay, Calendar endDay) throws ParseException {
		return calculateDays(startDay, endDay, true);
	}

	/**
	 * 有多少休息天数，给一个开始结束时间
	 * 
	 * @return
	 * @throws ParseException
	 */
	public List<String> restDays(String beginTime, String endTime) throws ParseException {
		Calendar startDay = Calendar.getInstance();
		Calendar endDay = Calendar.getInstance();
		startDay.setTime(format2.parse(beginTime));
		endDay.setTime(format2.parse(endTime));

		return restDays(startDay, endDay);
	}

	/**
	 * 有多少休息天数，给一个月份
	 * 
	 * @return
	 * @throws ParseException
	 */
	public List<String> restDays(String beginTime) throws ParseException {
		Calendar startDay = Calendar.getInstance();
		startDay.setTime(format2.parse(beginTime + "-01"));
		int last = startDay.getActualMaximum(Calendar.DAY_OF_MONTH); // 获取本月最大天数
		Calendar endDay = Calendar.getInstance();
		endDay.setTime(format2.parse(beginTime + "-" + last));

		return restDays(startDay, endDay);
	}

	/**
	 * 有多少休息天数，给一个开始结束时间
	 * 
	 * @return
	 * @throws ParseException
	 */
	public List<String> restDays(Calendar startDay, Calendar endDay) throws ParseException {
		return calculateDays(startDay, endDay, false);
	}

	/**
	 * 有天数
	 * 
	 * @return
	 * @throws ParseException
	 */
	private List<String> calculateDays(Calendar startDay, Calendar endDay, Boolean isWorkDay) throws ParseException {
		// 若开始时间晚于结束时间，返回 null
		if (startDay.getTime().getTime() > endDay.getTime().getTime()) {
			return null;
		}

		//
		List<String> result = new ArrayList<String>();
		while (startDay.getTime().getTime() <= endDay.getTime().getTime()) {
			boolean flag=true;
			if(startDay.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY || 
					startDay.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY){//判断日期是否是周六周日
				flag=false;
			}
			String day = format2.format(startDay.getTime());
			if (isWorkDay) {
				if ((!holiday.contains(day) && flag) || speworkday.contains(day)) {//非法定节假日、双休日或特殊工作日添加
					result.add(day);
				}
			} else {
				if (holiday.contains(day)) {
					result.add(day);
				}
			}
			startDay.add(Calendar.DAY_OF_MONTH, 1);
		}
		return result;
	}
}
